﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace CSharpHelpers.Commons
{
    /// <summary>
    /// 密码错误的原因
    /// </summary>
    public enum PasswordErrorType
    {
        Pass,//通过验证
        Length,//8個字符
        TreeCharType,//密碼必須包括A~Z, a~z, 0~9 和 ~!@#$^&*_其中三種字
        ContainUserName,// 密碼不能包含用戶名信息
        PswRepeatTreeChar,// 密碼不能使用3個重覆或3個順序連續的字符及數字
        PswDbRepeat,//新的密碼不能和前10次或者前18個月內的密碼相同
    }

    public class PasswordBusiness
    {
        /// <summary>
        /// 验证密码规则
        ///0.所有
        /// 1.密碼至少包括8個字符
        /// 2.密碼必須包括A~Z, a~z, 0~9 和 ~!@#$^&*_其中三種字符
        /// 3.密碼不能包含用戶名信息
        /// 4.密碼不能使用3個重覆或3個順序連續的字符及數字
        /// 5.新的密碼不能和前10次或者前18個月內的密碼相同
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <param name="pwd">密碼</param>
        /// <param name="encryptPwd">加密后的密码，由于不同项目加密算法有可能不同，所以这里需要传入加密后的密码</param>
        /// <param name="oldPwdList">前10次或者前18個月內的密碼，当为null时代表不验证前10次或者前18個月內的密碼</param>
        /// <param name="checkList">按格式传入值,如"0"代表所有,"1,3,4"代表只验证1,3,4种情况. 
        /// </param>
        /// <returns>返回错误类型</returns>
        public PasswordErrorType CheckAllPassword(string userName, string pwd, string encryptPwd, List<string> oldPwdList, string checkList = "0")
        {
            bool isPass = true;
            PasswordErrorType errorType = PasswordErrorType.Pass;

            //0代表所有
            if (checkList == "0")
            {
                checkList = "1,2,3,4,5";
            }

            string[] list = checkList.Split(',');

            foreach (string str in list)
            {
                switch (str)
                {
                    case "1":
                        isPass = CheckLength(pwd);
                        break;
                    case "2":
                        isPass = CheckPswChar(pwd);
                        break;
                    case "3":
                        isPass = CheckUserName(pwd, userName);
                        break;
                    case "4":
                        isPass = CheckPswRepeat(pwd);
                        break;
                    case "5":
                        isPass = CheckDbRepeat(encryptPwd, oldPwdList);
                        break;
                }

                //如果返回有值,证明校验失败,返回错误信息
                if (!isPass)
                {
                    errorType = (PasswordErrorType)Convert.ToInt32(str);
                    break;
                }
            }
            return errorType;
        }

        /// <summary>
        /// 密碼必須包括A~Z, a~z, 0~9 和 ~!@#$^&*_其中三種字符
        /// </summary>
        /// <param name="psw"></param>
        /// <returns></returns>
        private bool CheckPswChar(string psw)
        {
            bool isPass = true;
            int complex = 0;
            Regex regUpper = new Regex("[A-Z]");
            Regex regLower = new Regex("[a-z]");
            Regex regNumber = new Regex("[0-9]");
            Regex regStr = new Regex("[~!@#$^&*_]");

            if (regUpper.IsMatch(psw))
            {
                complex++;
            }
            if (regLower.IsMatch(psw))
            {
                complex++;
            }
            if (regNumber.IsMatch(psw))
            {
                complex++;
            }
            if (regStr.IsMatch(psw))
            {
                complex++;
            }
            if (complex < 3)
            {
                isPass = false;
            }
            return isPass;
        }

        /// <summary>
        /// 密碼至少包括8個字符
        /// </summary>
        /// <param name="psw"></param>
        /// <returns></returns>
        private bool CheckLength(string psw)
        {
            bool isPass = !string.IsNullOrEmpty(psw) && psw.Length >= 8;

            return isPass;
        }


        /// <summary>
        /// 密碼不能包含用戶名信息
        /// </summary>
        /// <param name="psw">密码</param>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        private bool CheckUserName(string psw, string userName)
        {
            //密碼不能包含用戶名信息
            bool isPass = !psw.Contains(userName);
            return isPass;
        }



        /// <summary>
        /// 字符必須為數字或者是字母-----用于判断密碼不能使用3個重覆或3個順序連續的字符及數字，并不是说真正的密码必須為數字或者是字母
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        private bool CheckNumberOrLetter(char c)
        {
            bool isSuccess = false;

            if (char.IsNumber(c))
            {
                isSuccess = true;
            }
            else if (char.IsUpper(c))
            {
                isSuccess = true;
            }
            else if (char.IsLower(c))
            {
                isSuccess = true;
            }

            return isSuccess;
        }

        /// <summary>
        /// 密碼不能使用3個重覆或3個順序連續的字符及數字
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="psw"></param>
        /// <returns></returns>
        private bool CheckPswRepeat(string psw)
        {
            bool isPass = true;

            if (psw.Length > 3)
            {
                int value1 = 1;
                int value2 = 3;
                int value3 = 5;

                for (int i = 0; i < psw.Length; i++)
                {
                    value1 = value2;
                    value2 = value3;
                    value3 = psw[i];

                    if (i >= 2)
                    {
                        //密碼不能使用3個重覆或3個順序連續的字符及數字
                        if (value1 == value2 && value2 == value3)
                        {
                            isPass = false;
                        }
                        else
                        {
                            //首先必須得是字母和數字
                            if (CheckNumberOrLetter((char)value1) && CheckNumberOrLetter((char)value2) && CheckNumberOrLetter((char)value3))
                            {
                                if ((value1 + 1 == value2 && value2 + 1 == value3) || (value1 - 1 == value2 && value2 - 1 == value3))
                                {
                                    isPass = false;
                                }
                            }
                        }
                    }
                }
            }

            return isPass;
        }

        /// <summary>
        /// 新的密碼不能和前10次或者前18個月內的密碼相同
        /// </summary>
        /// <param name="encryptPwd">加密后的密码</param>
        /// <param name="oldPwdList">前10次或者前18個月內的密碼</param>
        /// <returns></returns>
        private bool CheckDbRepeat(string encryptPwd, IEnumerable<string> oldPwdList)
        {
            bool isPass = oldPwdList != null && oldPwdList.Count(p => p == encryptPwd) > 0;
            return isPass;
        }
    }
}
